<?php
/**
 * 结算模型
 */
namespace Common\Model;
use Think\Model;

class BillModel extends Model{

    /**
     * 通过结算单号，获取结算详情
     * @param unknown $bill_sn
     */
    public function getDetail($bill_sn)
    {
        $map['bill_sn'] = $bill_sn;
        $data = $this->where($map)->find();
        return $data;
    }
    
    /**
     * 结算
     * @param unknown $date 日期
     * @return number 返回结算的条目数
     */
   public function doSettlement($date)
   {
       $now_date = date("Y-m-d", strtotime($date));
       $month_begin = strtotime("first day of {$now_date} 00:00:00");
       $month_end = strtotime("last day of {$now_date} 23:59:59");
       //获取相关数据
       $data = $this->analyseOrders($month_begin, $month_end);
       $s = $data['summary'];
       $i = 0;
       foreach ($s as $v) {
           $data_save = $v;
           $data_save['bill_sn'] = $sn = $this->getSN();
           $data_save['money_total'] = $v['money_ali'] + $v['money_weixin'] - $v['refund_ali'] - $v['refund_weixin'] - $v['fee_ali'] - $v['fee_weixin'];
           $data_save['start_time'] = $month_begin;
           $data_save['end_time'] = $month_end;
           $data_save['ctime'] = time();
           $id = $this->add($data_save);
           if(!$id) {
               return $i;
           } else {
               $items = $data['pay_detail'][$v['host_name']] + $data['refund_detail'][$v['host_name']];
               foreach($items as &$v1) {
                   $v1['ctime'] = NOW_TIME;
                   $v1['bill_sn'] = $sn;
               }
               unset($v1);
               M('bill_order')->addAll($items);
               $i++;
               
           }
       }
       
       return $i;
   }
   
   /**
    * 删除某影院的结算数据
    * 
    * @param unknown $date
    * @param unknown $host_name
    */
   public function delSettlement($date, $host_name=null)
   {
       $now_date = date("Y-m-d", strtotime($date));
       $month_begin = strtotime("first day of {$now_date} 00:00:00");
       $month_end = strtotime("last day of {$now_date} 23:59:59");
       
       $map['start_time'] = $month_begin;
       $map['end_time'] = $month_end;
       if ($host_name) {
           $map['host_name'] = $host_name;
       }
       $map['status'] = 0; //不允许删除已结算的数据
       $map['is_sync'] = 0;//不允许删除已同步的数据
       
       return $this->where($map)->delete();
   }
   
   /**
    * 同步数据到影院
    * 
    * 假设影院数量不会太大
    * @return number 完成同步的数量
    */
   public function sync()
   {
       //获取未同步的数据
       $map['host_name'] = array('NEQ', '');
       $map['is_sync'] = 0;
       $data = $this->where($map)->order('id ASC')->select();
       $i = 0;
       foreach ($data as $v) {
           $map_h['host_name'] = $v['host_name'];
           $host_ip = M('cinema_host')->where($map_h)->getField('host_ip');
           if ($host_ip) {
               $params = $v;
               unset($params['status']);
               unset($params['is_sync']);
               $params['class'] = 'CinemaBill';
               $params['method'] = 'sync';
               $params['host_ip'] = $host_ip;
               
               //获取结算相关的订单数据
               $params['order_list'] = M('bill_order')->where("bill_sn = '{$params['bill_sn']}'")->select();
               $res = callCinemaInterfaceData($params);
               if ($res['res'] == 0 && $res['data'] != 0 ) {
                   $this->where("id = {$params['id']}")->setField('is_sync', 1);
                   $i++;
               }
           }
       }
       
       return $i++;
   }
   
   /**
    * 生成结算序列号
    * 
    * 规则: "JS"+"6位日期"+"5位ID号"
    * 注：不支持并发执行
    */
   private function getSN()
   {
       $prefix = "JS" . date("ymd");
       
       //末位自增
       $map['bill_sn'] = array("LIKE", "{$prefix}%");
       $data = $this->where($map)->order("id DESC")->find();
       if ($data) {
           $num = intval(str_replace($prefix, '', $data['bill_sn'])) + 1;
       } else {
           $num = 1;
       }
       
       //补足五位
       return $prefix . str_pad($num, 5, '0', STR_PAD_LEFT);
   }
   
   /**
    * 统计时间段内的订单
    * @param unknown $start_time
    * @param unknown $end_time
    * @return multitype:
    */
   private function analyseOrders($start_time, $end_time)
   {
       $pay = $this->_analyseGetData($start_time, $end_time, 'pay');
       $refund = $this->_analyseGetData($start_time, $end_time, 'refund');
        
        //--------------------合并付款和退款数据--------------
       $host_collection = array_unique((array_keys($pay)+array_keys($refund)));
       
       $res = array();
       foreach($host_collection as $v) {
           $res['summary'][$v] = array(
                   'host_name' => $v,
                   'money_ali' => floatval($pay[$v]['ali']), 
                   'money_weixin' => floatval($pay[$v]['weixin']),
                   'refund_ali' => floatval($refund[$v]['ali']),
                   'refund_weixin' => floatval($refund[$v]['weixin']),
                   'fee_ali' => 0,
                   'fee_weixin' => 0,
           );
           
           //付款详情
           $res['pay_detail'][$v] = $pay[$v]['list'] ? $pay[$v]['list'] : array();
           
           //退款详情
           $res['refund_detail'][$v] = $refund[$v]['list'] ? $refund[$v]['list'] : array();
       }
       
        //获取手续费费率
        $fee['class'] = 'Common';
        $fee['method'] = 'getPayFee';
        $fee_ret = callWebInterfaceData($fee);
        if($fee_ret['res'] === 0) {
            $ali_fee_p = (float)$fee_ret['result']['ali'];
            $weixin_fee_p = (float)$fee_ret['result']['weixin'];
        } else {
            Throw new  \Exception("Not valid response from host [" . C('HOST_WEB') . '], ' . implode('', $fee_ret));
        }
        //计算手续费
        if($ali_fee_percent || $weixin_fee_percent) {
            //手续费计算
            foreach($res['summary'] as &$v) {
                //支付宝手续费
                $v['fee_ali'] = $ali_fee_p * ($v['money_ali'] - $v['refund_ali']) / 100;
                //微信手续费
                $v['fee_weixin'] = $weixin_fee_p * ($v['money_weixin']- $v['refund_weixin']) / 100;
            }
            unset($v);
        }
        
       return $res;
   }
   
   /**
    * 从数据库中读取数据
    * @param unknown $start_time
    * @param unknown $end_time
    * @param string $type
    */
   private function _analyseGetData($start_time, $end_time, $type='pay')
   {
       $ali_method = 6;
       $weixin_method = 7;
        
       //---------------收款------------
       $map['mt_order_pay_method.method'] = array('IN', array($weixin_method, $ali_method));
       if ($type=='pay') {
           $map['mt_order_pay_method.time'] = array('BETWEEN', array($start_time, $end_time));
       } else if ($type=='refund') {
           $map['mt_order_pay_method.refund'] = array('BETWEEN', array($start_time, $end_time));
       } else {
           Throw new  \Exception("Not valid type[pay or refund]: {$type} is given");
       }
       
       $sub_sql = M('order_pay_method')
       ->field('mt_order.host_name, mt_order.cinema_order_sn, mt_order_pay_method.*')
       ->join('left join mt_order on mt_order.net_order_sn=mt_order_pay_method.net_order_sn')
       ->where($map)->buildSql();
       $data = M()->field("host_name, method, sum(money) money")
       ->table($sub_sql.' a')->group("host_name, method")->select();
       
       $res = array();
       $host_collection = array();
       foreach($data as $v) {
           //支付宝
           if($v['method'] == $ali_method) {
               $res[$v['host_name']]['ali'] += $v['money'];
           } else if($v['method'] == $weixin_method) { //微信支付
               $res[$v['host_name']]['weixin'] += $v['money'];
           }
       }
       
       //获取影院结算的订单号 , 订单号（集合）, 同一个订单同一种支付方式只能有一个
       $detail = M()->field("host_name, net_order_sn, cinema_order_sn, method, money")
       ->table($sub_sql.' a')->group("host_name, net_order_sn")->select();
       foreach($detail as $v) {
           $res[$v['host_name']]['list'][] = array(
                   'net_order_sn' => $v['net_order_sn'],
                   'cinema_order_sn' => $v['cinema_order_sn'],
                   'method' => $v['method'],
                   'money' => $v['money'],
                   'is_refund' => ($type=='refund') ? 1 : 0
           );
       }
       
       return $res;
   }
}